package com.gitee.wsl.flow.transform


import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.transformWhile
import kotlin.experimental.ExperimentalTypeInference

/**
 * Collects all items of the upstream into a list.
 */
fun <T> Flow<T>.toListOnFlow() : Flow<List<T>> {
    val self = this
    return flow {
        val list = ArrayList<T>()
        self.collect {
            list.add(it)
        }
        emit(list)
    }
}

@OptIn(ExperimentalTypeInference::class)
inline fun <T, R> Flow<T>.transform(
    @BuilderInference crossinline transform: suspend FlowCollector<R>.(value: T) -> Unit
): Flow<R> = flow {
    collect { value ->
        return@collect transform(value)
    }
}

inline fun <T> Flow<T>.filter(crossinline predicate: suspend (T) -> Boolean): Flow<T> = transform { value ->
    if (predicate(value)) return@transform emit(value)
}

@OptIn(ExperimentalCoroutinesApi::class)
fun <T> Flow<T>.onEachLatest(block:suspend (T)->Unit) = transformLatest {
    block(it)
    emit(it)
}

fun Flow<Unit>.withStartEmit(): Flow<Unit> {
    return flow {
        emit(Unit)
        emitAll(this@withStartEmit)
    }
}

/**
 * Returns first element that is instance of specific type parameter [R], or `null` if not found
 */
suspend inline fun <reified R> Flow<*>.firstInstance(): R? = filterIsInstance<R>().firstOrNull()

/**
 * Take elements from the flow until the predicate is true.
 *
 * @param predicate The predicate.
 * @return The new flow.
 */
fun <T> Flow<T>.takeUntil(predicate: (T) -> Boolean): Flow<T> = transformWhile {
    emit(it)
    !predicate(it)
}